#include <libdwarf.h>
#include <dwarf.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int travel_cu(Dwarf_Debug dbg);
int travel_die_indeep(Dwarf_Debug dbg, Dwarf_Die root_die, Dwarf_Bool is_info, int level);
int print_die_debug_info(Dwarf_Die in_die, int level);

int main()
{
    // 初始化dwarf文件
    // const char *filepath = "./foo.dSYM/Contents/Resources/DWARF/foo";
    const char *filepath = "../SymbolicatedDemo.app.dSYM/Contents/Resources/DWARF/SymbolicatedDemo";
    char real_path[200];
    Dwarf_Handler errhand = 0;
    Dwarf_Ptr errarg = 0;
    Dwarf_Debug dbg = 0;
    Dwarf_Error error;
    dwarf_init_path(filepath, real_path, 200, DW_GROUPNUMBER_ANY, errhand, errarg, &dbg, &error);

    // 遍历CompileUnit
    travel_cu(dbg);

    return 0;
}

int travel_cu(Dwarf_Debug dbg)
{

    Dwarf_Bool is_info = 1;
    Dwarf_Unsigned header_len;
    Dwarf_Half version_stamp;
    Dwarf_Off abbrev_off;
    Dwarf_Half address_size;
    Dwarf_Half length_size;
    Dwarf_Half extension_size;
    Dwarf_Sig8 type_signature;
    Dwarf_Unsigned type_off;
    Dwarf_Unsigned next_cu_header_off;
    Dwarf_Half header_cu_type;
    Dwarf_Error error;

    while (1)
    {
        int res = DW_DLV_ERROR;
        res = dwarf_next_cu_header_d(
            dbg,
            is_info,
            &header_len,
            &version_stamp,
            &abbrev_off,
            &address_size,
            &length_size,
            &extension_size,
            &type_signature,
            &type_off,
            &next_cu_header_off,
            &header_cu_type,
            &error);
        if (res == DW_DLV_NO_ENTRY)
        {
            break;
        }

        // 获取第一个DIE
        Dwarf_Die no_die = 0;
        Dwarf_Bool is_info = 1;
        Dwarf_Die sub_die = 0;
        dwarf_siblingof_b(dbg, no_die, is_info, &sub_die, &error);

        travel_die_indeep(dbg, sub_die, is_info, 0);
    }

    return 0;
}

int travel_die_indeep(Dwarf_Debug dbg, Dwarf_Die root_die, Dwarf_Bool is_info, int level)
{
    print_die_debug_info(root_die, level);

    Dwarf_Die cur_die = root_die;
    Dwarf_Error error;

    // 处理sibling
    while (1)
    {
        // 处理Child
        Dwarf_Die child_die = 0;
        int ret = dwarf_child(cur_die, &child_die, &error);
        if (ret == DW_DLV_OK)
        {
            travel_die_indeep(dbg, child_die, is_info, level + 1);
        }

        Dwarf_Die sub_die;
        ret = dwarf_siblingof_b(dbg, cur_die, is_info, &sub_die, &error);
        if (ret == DW_DLV_OK)
        {
            print_die_debug_info(sub_die, level);
            cur_die = sub_die;
        }
        else
        {
            break;
        }
    }

    return 0;
}

char *_blank_string(int level)
{
    int cnt = level * 2;
    char *blank = malloc(cnt * sizeof(char));
    memset(blank, ' ', cnt);
    return blank;
}

const char *_get_tag_name(Dwarf_Half tag)
{
    switch (tag)
    {
    case DW_TAG_compile_unit:
        return "DW_TAG_compile_unit";
        break;
    case DW_TAG_subprogram:
        return "DW_TAG_subprogram";
        break;
    case DW_TAG_variable:
        return "DW_TAG_variable";
        break;
    case DW_TAG_formal_parameter:
        return "DW_TAG_formal_parameter";
        break;
    // case DW_TAG_member:
    //     return "DW_TAG_member";
    //     break;
    default:
        return "";
        break;
    }
}

int print_die_debug_info(Dwarf_Die in_die, int level)
{
    Dwarf_Half tag;
    Dwarf_Error error;
    dwarf_tag(
        in_die,
        &tag,
        &error);

    const char *tag_name = _get_tag_name(tag);
    if (strlen(tag_name) == 0)
    {
        return 0;
    }

    char *name = 0;
    dwarf_diename(in_die, &name, &error);
    char *blank = _blank_string(level);

    printf("%s<die tag=%s, name=%s>\n", blank, tag_name, name);
    free(blank);

    return 0;
}

